package main

/*
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
struct args{   int p1,p2;   int r;  };
int add(struct args* arg) {   arg->r= arg->p1 + arg->p2;   return 100;  }
*/
import "C"
import (
	"fmt"
	"time"
	"unsafe"
)

//go:linkname asmcgocall runtime.asmcgocall
func asmcgocall(unsafe.Pointer, uintptr) int32

// 要从Go中调用C函数，cgo生成的代码会调用 runtime.cgocall(_cgo_Cfunc_f, frame)，其中_cgo_Cfunc_f对应的C函数。
//
//runtime.cgocall会调用 entersyscall 进入系统调用以避免阻塞其余协程的调度或则垃圾回收器。然后调用 runtime.asmcgocall(_cgo_Cfunc_f, frame)。
//
//runtime.asmcgocall 是汇编实现的，该函数会切换内核线程的 g0 栈(也就是操作系统分配的堆栈)，因此可以安全的运行gcc编译的代码以及调用_cgo_Cfunc_f。
//
//_cgo_Cfunc_f会调用实际的C函数，并拿到执行的结果，然后返回给runtime.asmcgocall。
//
//等当前协程重新获取控制后，runtime.asmcgocall 会切换回原来的go协程的栈，并返回到runtime.cgocall.。
//
//等当前协程重新获取控制后， runtime.cgocall会调用exitsyscall，该函数会阻塞直到m能够运行当前协程。
// g0和m0的作用：https://zhuanlan.zhihu.com/p/213745994.
func main() {
	arg := C.struct_args{}
	arg.p1 = 100
	arg.p2 = 200

	nanoSeconds := time.Now().Nanosecond()
	fmt.Println(nanoSeconds)
	for i := 0; i < 1000000; i++ {
		//C.add(&arg)                                        // 75278200
		asmcgocall(C.add, uintptr(unsafe.Pointer(&arg))) // 16003600
		// fmt.Println(arg.r)
	}
	fmt.Println(time.Now().Nanosecond())
	fmt.Println(time.Now().Nanosecond() - nanoSeconds)
	//C.add(&arg)
	//asmcgocall(C.add, uintptr(unsafe.Pointer(&arg)))
	//fmt.Println(arg.r)
}
